﻿using System;
using System.Collections.Generic;

namespace MessageTools
{
    /// <summary>
    /// 控制台消息缩进样式的枚举
    /// </summary>
    public enum IndentationType { None = 0, Forward = 1, Back = 2, ArrowForward = 3, ArrowBack = 4 }

    /// <summary>
    /// 状态消息，包含一条消息文本和消息的重要等级
    /// </summary>
    public class StatusMessage
    {
        /// <summary>
        /// 默认构造方法
        /// </summary>
        public StatusMessage()
        {

        }

        /// <summary>
        /// 构造方法
        /// </summary>
        /// <param name="Text">消息文本</param>
        /// <param name="MessageSource">消息来源</param>
        public StatusMessage(string Text, string MessageSource)
        {
            MessageText = Text;
            this.MessageSource = MessageSource;
        }

        /// <summary>
        /// 析构函数：通知所有已绑定的IDisplayUnit对象与此状态消息解绑
        /// </summary>
        ~StatusMessage()
        {
            /**用foreach会抛出异常：System.InvalidOperationException*/
            for (int i = 0; i < AttachedDisplayUnits.Count; i++)
            {
                try
                {
                    RemoveUnit(AttachedDisplayUnits[i]);
                }
                catch (Exception)
                { }
            }
        }


        /// <summary>
        /// 消息来源
        /// </summary>
        public string MessageSource { get; set; } = string.Empty;

        /// <summary>
        /// 消息文本
        /// </summary>
        public string MessageText { get; set; } = string.Empty;

        /// <summary>
        /// 消息等级
        /// </summary>
        public int Level { get; set; } = 0;

        /// <summary>
        /// 获取或设置是否在控制台回显
        /// </summary>
        public bool Echo { get; set; } = true;

        /// <summary>
        /// 获取或设置是否在消息文本中加入时间戳
        /// </summary>
        public bool TimeStamp { get; set; } = true;

        private int indentation = 0;
        /// <summary>
        /// 消息显示整体缩进数量
        /// </summary>
        public int Indentation
        {
            get { return indentation; }
            private set { indentation = value >= 0 ? value : 0; }
        }

        private int indentationDistance = 2;
        /// <summary>
        /// 消息缩进的单位数量
        /// </summary>
        public int IndentationDistance
        {
            get { return indentationDistance; }
            private set { indentationDistance = value >= 1 ? value : 1; }
        }


        /// <summary>
        /// 默认消息等级字体前景色
        /// </summary>
        private readonly ConsoleColor[] ColorSet = { ConsoleColor.Green, ConsoleColor.Blue, ConsoleColor.DarkYellow, ConsoleColor.Red };
        
        /// <summary>
        /// 默认消息前缀
        /// </summary>
        public static readonly string[] MessagePrefixs = { "成功", "信息", "警告", "错误" };
        
        /// <summary>
        /// 默认消息前缀数量
        /// </summary>
        private static readonly int PrefixsCount = 4;
        
        /// <summary>
        /// 已绑定的消息展示对象对象列表
        /// </summary>
        public readonly List<IDisplayUnit> AttachedDisplayUnits = new List<IDisplayUnit>();
        
        /// <summary>
        /// 抛出异常信息
        /// </summary>
        /// <param name="ex">要抛出的Exception对象</param>
        public virtual void ShowException(Exception ex)
        {
            throw (ex);
        }
        
        /// <summary>
        /// 通知所有已绑定的消息展示对象更新消息
        /// </summary>
        /// <param name="Text">消息文本</param>
        /// <param name="Level">消息等级</param>
        private void RefreshAttachedDisplayUnits(string Text, int Level)
        {
            foreach (var item in AttachedDisplayUnits)
            {
                try
                {
                    item.UpdateInfo(Text, Level);
                }
                catch (Exception ex)
                {
                    ShowException(ex);
                }
            }
        }
        
        /// <summary>
        /// 绑定新的消息展示对象
        /// </summary>
        /// <param name="Unit">要绑定的消息展示对象</param>
        public void AttachUnit(IDisplayUnit Unit)
        {
            AttachedDisplayUnits.Add(Unit);
            Unit.AttachedMessage.Add(this);
        }
        
        /// <summary>
        /// 解绑消息展示对象
        /// </summary>
        /// <param name="Unit">要解绑的消息展示对象</param>
        public void RemoveUnit(IDisplayUnit Unit)
        {
            Unit.AttachedMessage.Remove(this);
            AttachedDisplayUnits.Remove(Unit);
        }
        
        /// <summary>
        /// 格式化消息文本
        /// </summary>
        /// <param name="Text">消息文本</param>
        /// <param name="IndentationType">缩进样式</param>
        /// <returns></returns>
        private string FormatText(string Text, IndentationType IndentationType)
        {
            string Prefix = string.Empty;
            switch (IndentationType)
            {
                case IndentationType.None: break;
                case IndentationType.Forward:
                    Indentation += IndentationDistance;
                    for (int i = 0; i < Indentation; i++)
                        Prefix += " ";
                    break;
                case IndentationType.Back:
                    Indentation -= IndentationDistance;
                    for (int i = 0; i < Indentation; i++)
                        Prefix += " ";
                    break;
                case IndentationType.ArrowForward:
                    Indentation += IndentationDistance;
                    for (int i = 0; i < Indentation - 1; i++)
                        Prefix += " ";
                    Prefix += ">";
                    break;
                case IndentationType.ArrowBack:
                    Indentation -= IndentationDistance;
                    for (int i = 0; i < Indentation - 1; i++)
                        Prefix += " ";
                    Prefix += "<";
                    break;
            }
            if (TimeStamp)
                Prefix += "[" + DateTime.Now.ToString("G") + " " + MessageSource + "]: ";
            return Prefix + Text;
        }
        
        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="Text">消息文本</param>
        /// <param name="Level">消息等级</param>
        public void SendMessage(string Text, int Level)
        {
            SendMessage(Text, Level, IndentationType.None);
        }
        
        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="Text">消息文本</param>
        /// <param name="Level">消息等级</param>
        /// <param name="IndentationType">消息文本缩进样式</param>
        public void SendMessage(string Text, int Level, IndentationType IndentationType)
        {
            try
            {
                this.Level = Level;
                MessageText = Text;
                Text = FormatText(Text, IndentationType);
                if (Echo)
                {
                    Console.ForegroundColor = ColorSet[Level % PrefixsCount];
                    Console.Write(MessagePrefixs[Level % PrefixsCount] + ": ");
                    Console.ForegroundColor = ConsoleColor.White;
                    Console.WriteLine(Text);
                }
                RefreshAttachedDisplayUnits(Text, Level);
            }
            catch (LevelOutOfRangeException)
            {
                ShowException(new LevelOutOfRangeException("消息等级超限"));
            }
            catch (Exception ex)
            {
                ShowException(ex);
            }
        }


        /// <summary>
        /// 表示当前状态消息对象的字符串
        /// </summary>
        /// <returns></returns>
        override public string ToString()
        {
            return "Message=" + MessageText + ", CurrentLevel=" + Level;
        }
    }
}